Skip to content

toc - update active link in toc before expanding#9674

Merged
cderv merged 3 commits into
mainfrom
fix/toc-expand
May 15, 2024
Merged

toc - update active link in toc before expanding#9674
cderv merged 3 commits into
mainfrom
fix/toc-expand

Conversation

@cderv

@cderv cderv commented May 15, 2024

Copy link
Copy Markdown
Member

toc expansion is controled by toc-expand and computed on client side once at initialization and then at scrolling. It is based on links in the toc that are active. Default is first link to be the active one.

When no scrolling (body content height smaller than window height), we need to update the active link to be last section seen on screen, and do this before we expand the toc.Welcome to the quarto GitHub repo!

solves #3178

Context

Active link in TOC is updated based on window and body size (to detect scroll bar) or based on scroll position

const updateActiveLink = () => {
// The index from bottom to top (e.g. reversed list)
let sectionIndex = -1;
if (
window.innerHeight + window.pageYOffset >=
window.document.body.offsetHeight
) {
sectionIndex = 0;
} else {
sectionIndex = [...sections].reverse().findIndex((section) => {
if (section) {
return window.pageYOffset >= section.offsetTop - sectionMargin;
} else {
return false;
}
});
}
if (sectionIndex > -1) {
const current = sections.length - sectionIndex - 1;
if (current !== currentActive) {
removeAllActive();
currentActive = current;
makeActive(current);
if (init) {
window.dispatchEvent(sectionChanged);
}
init = true;
}
}
};

and expansion is done based on which ul from the toc has active link

// Walk the TOC and collapse/expand nodes
// Nodes are expanded if:
// - they are top level
// - they have children that are 'active' links
// - they are directly below an link that is 'active'
const walk = (el, depth) => {
// Tick depth when we enter a UL
if (el.tagName === "UL") {
depth = depth + 1;
}
// It this is active link
let isActiveNode = false;
if (el.tagName === "A" && el.classList.contains("active")) {
isActiveNode = true;
}
// See if there is an active child to this element
let hasActiveChild = false;
for (child of el.children) {
hasActiveChild = walk(child, depth) || hasActiveChild;
}
// Process the collapse state if this is an UL
if (el.tagName === "UL") {
if (tocOpenDepth === -1 && depth > 1) {
el.classList.add("collapse");
} else if (
depth <= tocOpenDepth ||
hasActiveChild ||
prevSiblingIsActiveLink(el)
) {
el.classList.remove("collapse");
} else {
el.classList.add("collapse");
}
// untick depth when we leave a UL
depth = depth - 1;
}
return hasActiveChild || isActiveNode;
};
// walk the TOC and expand / collapse any items that should be shown

This works ok, but this is currently how we initialize

// walk the TOC and expand / collapse any items that should be shown
if (tocEl) {
updateActiveLink();
walk(tocEl, 0);
}

and we only update on scrolling

// Throttle the scroll event and walk peridiocally
window.document.addEventListener(
"scroll",
throttle(() => {
if (tocEl) {
updateActiveLink();
walk(tocEl, 0);
}
if (!isReaderMode()) {
hideOverlappedSidebars();
}
}, 5)
);

Notice the different order.

For the no scroll case, expanding before updating the active link does not work. This is because first link is made active by default, but then the update should see the no scroll bar and update the last link to be the active one. Then expansion should happen.

When we scroll this is the order - update active link then expand.

I think we should initialize the same way.

I also added this to the resize event because when windows is resized to a no scroll bar state, the toc expansion won't be updated anymore.

Hard to test in several cases, but I did test locally and I believe this change is safe going through the code logic in my head for what could happen.

cderv added 2 commits May 15, 2024 11:58
toc expansion is controled by `toc-expand` and computed on client side once at initialization and then at scrolling.  It is based on links in the toc that are active. Default is first link to be the active one.

When no scrolling (body content height smaller than window height),  we need to update the active link to be last section seen on screen, and do this before we expand the toc.
In case resize leads to no scroll bar, the toc should be expanded to show all the content.
@cderv cderv linked an issue May 15, 2024 that may be closed by this pull request
4 tasks
[skip ci]
@cderv cderv merged commit b35914c into main May 15, 2024
@cderv cderv deleted the fix/toc-expand branch May 15, 2024 12:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TOC ignoring subsections from child document

1 participant